import shutil
from datetime import date
from pathlib import Path

from django.apps import apps
from django.conf import settings
from django.core.management import BaseCommand, call_command
from django.template.loader import render_to_string


class Command(BaseCommand):
    """
    A Django management command to generate model diagrams and documentation.
    This command performs the following tasks:
    - Cleans existing model documentation.
    - Generates diagrams for models in the specified apps.
    - Creates Markdown documentation for each model, including fields and constraints.
    - Generates an index file for the model documentation.
    Usage:
        Run this command using `python manage.py generate_model_diagrams`.
    """

    help = 'Generate model diagrams'
    model_doc_template = 'model_doc.html'
    model_doc_index_template = 'model_doc_index.html'

    @property
    def root_path(self):
        return Path(settings.ROOT)

    @property
    def docs_path(self):
        return self.root_path / 'docs'

    @property
    def model_doc_path(self):
        return self.docs_path / 'topics' / 'models'

    def clean_model_docs_dir(self):
        self.stdout.write(f'Emptying {self.model_doc_path}')
        if self.model_doc_path.exists():
            shutil.rmtree(self.model_doc_path)
        self.model_doc_path.mkdir(parents=True)

    def modelgraph(self, app_name, dir_name):
        dot_file = self.model_doc_path / dir_name / ('graph' + '.dot')
        dot_file.parent.mkdir(parents=True, exist_ok=True)
        call_command(
            'graph_models',
            app_name,
            outputfile=dot_file.as_posix(),
            group_models=app_name != '--all',
            relation_fields_only=True,
        )

    def modelinfo(self, app_name, model_name):
        doc_path: Path = self.model_doc_path / app_name / (model_name + '.md')
        doc_path.parent.mkdir(parents=True, exist_ok=True)
        call_command(
            'modelinfo',
            f'{app_name}.{model_name}',
            output=doc_path.as_posix(),
            exclude_defaults=True,
        )
        with doc_path.open('a') as doc:
            timestamp = date.today().isoformat()
            doc.write(f'\n!!! THIS DOCUMENT WAS *AUTOGENERATED* ON {timestamp} !!!\n')

    def create_index(self, name: str, path: Path, model_list: list[str]):
        index_path = path / 'index.md'
        index_path.write_text(
            render_to_string(
                self.model_doc_index_template,
                {
                    'name': name,
                    'models': model_list,
                },
            )
        )

    def handle(self, *args, **options):
        self.clean_model_docs_dir()

        self.modelgraph(app_name='--all', dir_name='')

        apps_with_models = [
            app
            for app in apps.get_app_configs()
            if app.name.startswith('olympia.')
            and app.models_module
            and len(app.models.items()) > 0
        ]
        app_index = []

        for app in apps_with_models:
            app_name = app.name.split('.')[-1]
            self.modelgraph(app_name=app_name, dir_name=app_name)

            app_index.append(f'{app_name}/index')
            app_models = []

            for model in app.get_models():
                model_name = model.__name__
                self.modelinfo(app_name=app_name, model_name=model_name)
                if model_name not in app_models:
                    app_models.append(model_name)

            self.create_index(
                name=app_name,
                path=(self.model_doc_path / app_name),
                model_list=app_models,
            )

        self.create_index(name='Models', path=self.model_doc_path, model_list=app_index)
